package com.kgc.scd.util;

import com.kgc.scd.config.SensitiveWordCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * Created On : 2022/11/12.
 * <p>
 * Author : zhukang
 * <p>
 * Description: 敏感词工具类
 */
@Component
public class SensitiveWordFilter {
    // 最小匹配规则
    public static final int MIN_MATCH_TYPE = 1;

    // 最大匹配规则
    public static final int MAX_MATCH_TYPE = 2;

    @Autowired
    private SensitiveWordCache sensitiveWordCache;

    /**
     * @param : [java.lang.String]
     * @return : boolean true 包含；false 不包含
     * @author : zhukang
     * @date : 2022/11/12
     * @description : 是否包含敏感词(默认按最小匹配规则来，只要有敏感词就ok)，最小匹配规则
     */
    public boolean isContainSensitiveWordMin(String txt) {
        return isContainSensitiveWord(txt, MIN_MATCH_TYPE);
    }

    /**
     * @param : [java.lang.String, int]
     * @return : boolean
     * @author : zhukang
     * @date : 2022/11/12
     * @description : 是否包含敏感词(默认按指定匹配规则来，只要有敏感词就ok)
     * 如果敏感词库为：
     * * 傻叉
     * * 傻叉人
     * * 大傻叉
     * * 初始化之后为：{傻={叉={人={isEnd=1}, isEnd=1}, isEnd=0}, 大={傻={叉={isEnd=1}, isEnd=0}, isEnd=0}}
     * * 1、按最小规则匹配，  匹配 傻叉 的时候，匹配到叉，就为最后一个了 直接break。如果输入的敏感词是傻叉人，命中的只是傻叉，而不是傻叉人
     * * 2、按最大规则匹配，  匹配 傻叉 的时候，匹配到叉，已经为最后一个，但是按照最大规则，会继续匹配人，命中的是傻叉人
     * * 3、如果关键词是傻叉猫，两种匹配规则都会匹配到傻叉，如果输入的是傻叉人猫，按最小规则匹配是傻叉，按最大规则匹配傻叉人
     */
    public boolean isContainSensitiveWord(String txt, int matchType) {
        if (txt == null || "".equals(txt)) {
            return false;
        }
        for (int i = 0; i < txt.length(); i++) {
            int matchFlag = this.checkSensitiveWords(txt, i, matchType);
            if (matchFlag > 0) {
                return true;
            }
        }
        return false;
    }

    /**
     * @param : 待判断文本 起始位置 匹配类型： 1 最小匹配原则；2 最大匹配原则
     * @return : int 大于0表示包含敏感词且表示敏感词匹配长度，否则不包含
     * @author : zhukang
     * @date : 2022/11/12
     * @description : 校验是否包含敏感词
     */
    private int checkSensitiveWords(String txt, int beginIndex, int matchType) {
        // 敏感词结束标识位：用于敏感词只有1位的情况
        boolean flag = false;

        // 匹配标识数默认为0
        int matchFlag = 0;

        // 从缓存中，获取敏感词库
        Map<String, Object> nowMap = sensitiveWordCache.getSensitiveWordMap();

        // 循环判断是否存在敏感词
        for (int i = beginIndex; i < txt.length(); i++) {
            // 获取第一个字符
            char word = txt.charAt(i);
            
            // 获取指定key，判断当前字符是不是一个敏感词的开头
            nowMap = (Map) nowMap.get(String.valueOf(word));

            // 不存在，直接返回
            if (nowMap == null) {
                break;
            }

            // 根据排列组合的匹配，如果出现在敏感词库中，即找到相应key，匹配标识+1
            matchFlag++;

            // 如果已经匹配到词库中完整的敏感词, 改匹配结束标识，并根据匹配规则判断，是否继续
            if ("1".equals(nowMap.get("isEnd"))) {
                // 结束标志位为true，已经命中到了一个完整敏感词
                flag = true;

                // 最小规则，直接返回, 最大规则还需继续查找
                if (matchType == MIN_MATCH_TYPE) {
                    break;
                }
            }
        }

        // 长度必须大于等于1，为词，敏感词只有1个字的情况
        if (matchFlag < 2 || !flag) {
            matchFlag = 0;
        }

        return matchFlag;
    }

    /**
     * @author : zhukang
     * @date   : 2022/11/12
     * @param  : [txt 待判断文本, matchType 匹配类型： 1 最小匹配原则；2 最大匹配原则]
     * @return : java.util.Set<java.lang.String>
     * @description : 获取所有敏感词
     */
    public Set<String> getSensitiveWords(String txt, Integer matchType) {
        Set<String> sensitiveWords = new HashSet<>();

        for (int i = 0; i < txt.length(); i++) {
            Integer length = checkSensitiveWords(txt, i, matchType);
            if (length > 0) {
                sensitiveWords.add(txt.substring(i, i + length));
                // 循环i会+1，所以需-1
                i = i + length - 1;
            }
        }
        return sensitiveWords;
    }

    /**
     * @author : zhukang
     * @date   : 2022/11/12
     * @param  : [txt 文本, matchType 匹配类型： 1 最小匹配原则；2 最大匹配原则, replaceStr 替换字符]
     * @return : java.lang.String
     * @description : 替换敏感词
     */
    public String replaceSensitiveWords(String txt, Integer matchType, String replaceStr) {
        if (txt == null || "".equals(txt)) {
            return txt;
        }
        // 获取所有敏感词
        Set<String> sensitiveWords = getSensitiveWords(txt, matchType);
        Iterator<String> iterator = sensitiveWords.iterator();
        String replaceString = "";
        while (iterator.hasNext()) {
            String sWord = iterator.next();
            replaceString = getReplaceString(replaceStr, sWord.length());
            txt = txt.replaceAll(sWord, replaceString);
        }
        return txt;
    }

    /**
     * @param : [java.lang.String, java.lang.Integer]
     * @return : java.lang.String
     * @author : zhukang
     * @date : 2022/11/12
     * @description : 替换为指定字符，没有指定替换字符，默认*
     */
    private String getReplaceString(String replaceStr, Integer length) {
        // 指定替换字符为*
        if (replaceStr == null) {
            replaceStr = "*";
        }
        // 可变字符串对象
        StringBuffer replaceString = new StringBuffer();
        // 循环遍历，替换内容
        for (int i = 0; i < length; i++) {
            replaceString.append(replaceStr);
        }
        return replaceString.toString();
    }

}
